#ifndef TOKEN_H_
#define TOKEN_H_

#include <string>
#include <iostream>

/* intentionally left in global scope */
enum TokenType { TK_UNKNOWN, TK_ERROR_UNTERMINATED_COMMENT, TK_ERROR_NUM_CONST_TOO_LARGE,
	/* starting here the order of enumeration is very important for the scanner */
	TK_ERROR_CHAR_CONST, /* <-- ERROR_SENTRY all preceeding TokenTypes are error tokens */
	TK_IDENTIFIER, TK_NUM, TK_CHAR_CONST, 
	/* TokenTypes after this point silently ignore setValue() */
	TK_RIGHT_PARENTHESES, TK_LEFT_PARENTHESES, TK_SEMICOLON, TK_PLUS, TK_MINUS, TK_ASTERISK, 
	TK_SLASH, TK_PERCENT, TK_ASSIGNMENT, TK_LT, TK_EXCLAMATION, TK_LOGIC_OR, 
	TK_LOGIC_AND, TK_GT, TK_EQUAL, TK_LTE, TK_GTE, TK_NOT_EQ, 
	TK_RIGHT_BRACKET, TK_LEFT_BRACKET, TK_RIGHT_BRACE, TK_LEFT_BRACE, TK_COMMA, 
	TK_MAIN, TK_INT, TK_CHAR, TK_VOID, TK_IF, 
	TK_ELSE, TK_WHILE, TK_RETURN, TK_SCANF, TK_PRINTF,
	/* end of the specific ordering */
	TK_EOF,
	TOKEN_SENTRY // used for array sizing
};

class Token
{
public:
	/* this is the value of the first element that is in the same order as it's associated
	 * state in the scanner */
	static const int FIRST_TOKEN_STATE=TK_ERROR_CHAR_CONST;
	/* TokenTypes <= ERROR_SENTRY are all error tokens this constant should point to
	 * the last TokenType which describes an error */
	static const int ERROR_SENTRY=TK_ERROR_CHAR_CONST;
	
	Token(TokenType tokenType=TK_UNKNOWN);
	virtual ~Token();

	void setType(TokenType type);
	TokenType getType();
	
	void setLineNum(int lineNum);
	int getLineNum();
	
	void setValue(std::string value);
	std::string getValue();
		
	std::ostream& inserter(std::ostream &os) const;
	bool operator == (TokenType tkType) const;
	bool operator != (TokenType tkType) const;

private:
	TokenType type;
	int lineNumber;
	
	std::string sValue;
	
	// used for console output
	static std::string tokenNames[TOKEN_SENTRY];
};

/**
 * Insertion operator overloading for tokens
 */
inline std::ostream& operator << (std::ostream &os, const Token &tk) {
	return tk.inserter(os);
}

/**
 * Comparison operator overloading for token type, so that we can compare
 * a token with its type like: t == TK_IF
 */
inline bool Token::operator == (TokenType tkType) const {
	return type == tkType;
}

/**
 * Comparison operator overloading for token type
 */
inline bool Token::operator != (TokenType tkType) const {
	return type != tkType;
}

#endif /*TOKEN_H_*/
